package top.hekun.study.core.visitor.method.frame;

import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AnalyzerAdapter;
import top.hekun.study.core.visitor.method.complex.PatternMethodVisitor;

import java.util.Arrays;
import java.util.List;

import static org.objectweb.asm.Opcodes.IADD;
import static org.objectweb.asm.Opcodes.ICONST_0;

/**
 * 〈frame 分析〉
 *
 * @author: Sakura
 * @date: 2023/5/15
 */
public class MyMethodVisitor1 extends AnalyzerAdapter {
    private String methodName;
    private String methodDescriptor;

    public MyMethodVisitor1(String owner, int access, String name, String descriptor, MethodVisitor methodVisitor) {
        super(Opcodes.ASM9, owner, access, name, descriptor, methodVisitor);
        methodName = name;
        methodDescriptor = descriptor;
    }

    @Override
    public void visitCode() {
        super.visitCode();
        System.out.println("\r\n" + methodName + "" + methodDescriptor);
        printStackMapFrame();
    }

    @Override
    public void visitInsn(int opcode) {
        super.visitInsn(opcode);
        printStackMapFrame();
    }

    @Override
    public void visitIntInsn(int opcode, int operand) {
        super.visitIntInsn(opcode, operand);
        printStackMapFrame();
    }

    @Override
    public void visitVarInsn(int opcode, int var) {
        super.visitVarInsn(opcode, var);
        printStackMapFrame();
    }

    @Override
    public void visitTypeInsn(int opcode, String type) {
        super.visitTypeInsn(opcode, type);
        printStackMapFrame();
    }

    @Override
    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
        super.visitFieldInsn(opcode, owner, name, descriptor);
        printStackMapFrame();
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        printStackMapFrame();
    }

    @Override
    public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
        super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
        printStackMapFrame();
    }

    @Override
    public void visitJumpInsn(int opcode, Label label) {
        super.visitJumpInsn(opcode, label);
        printStackMapFrame();
    }

    @Override
    public void visitLdcInsn(Object value) {
        super.visitLdcInsn(value);
        printStackMapFrame();
    }

    @Override
    public void visitIincInsn(int var, int increment) {
        super.visitIincInsn(var, increment);
        printStackMapFrame();
    }

    @Override
    public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
        super.visitTableSwitchInsn(min, max, dflt, labels);
        printStackMapFrame();
    }

    @Override
    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
        super.visitLookupSwitchInsn(dflt, keys, labels);
        printStackMapFrame();
    }

    @Override
    public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {
        super.visitMultiANewArrayInsn(descriptor, numDimensions);
        printStackMapFrame();
    }

    private void printStackMapFrame() {
        String locals_str = locals == null ? "[]" : list2Str(locals);
        String stack_str = stack == null ? "[]" : list2Str(stack);
        String line = String.format("%s             %s", locals_str, stack_str);
        System.out.println(line);
    }

    private String list2Str(List<Object> list) {
        if (list == null || list.size() == 0) return "[]";
        int size = list.size();
        String[] array = new String[size];
        for (int i = 0; i < size; i++) {
            Object item = list.get(i);
            array[i] = item2Str(item);
        }
        return Arrays.toString(array);
    }

    private String item2Str(Object obj) {
        if (obj == Opcodes.TOP) {
            return "top";
        } else if (obj == Opcodes.INTEGER) {
            return "int";
        } else if (obj == Opcodes.FLOAT) {
            return "float";
        } else if (obj == Opcodes.DOUBLE) {
            return "double";
        } else if (obj == Opcodes.LONG) {
            return "long";
        } else if (obj == Opcodes.NULL) {
            return "null";
        } else if (obj == Opcodes.UNINITIALIZED_THIS) {
            return "uninitialized_this";
        } else if (obj instanceof Label) {
            Object value = uninitializedTypes.get(obj);
            if (value == null) {
                return obj.toString();
            } else {
                return "uninitialized_" + value;
            }
        } else {
            return obj.toString();
        }
    }
}
